using System;
using System.Collections.Generic;
using System.Text;

namespace RapidHDL
{
    public class PipelineMergerPriority : Component 
    {
        public Dictionary<int, PipelineBus> InputBuses = new Dictionary<int,PipelineBus>();
        public PipelineBus OutputBus;

        Dictionary<int, PipelineStage> dicPipesIn = new Dictionary<int,PipelineStage>();
        PipelineStage oPipeOut;

        int iWidth;
        ClockComponent oClock;
        NodeVector nvReset;

        public PipelineMergerPriority(Component poParentComponent, string psName, int piWidth, ClockComponent poClock, NodeVector pnvReset,int piInputs)
            : base (poParentComponent, psName)
        {
            iWidth = piWidth;
            oClock = poClock;
            nvReset = pnvReset;

            oPipeOut = new PipelineStage(this, "pipe_out", poClock, pnvReset, piWidth);
            OutputBus = oPipeOut.OutputBus;
            for (int iIdx = 0; iIdx < piInputs; iIdx++)
            {
                PipelineStage oPipeIn = new PipelineStage(this, "pipe_in_" + iIdx.ToString(), poClock, pnvReset, iWidth);
                dicPipesIn.Add(dicPipesIn.Count, oPipeIn);
                InputBuses.Add(InputBuses.Count, oPipeIn.InputBus);
            }
        }

        public override void GenerateStructure()
        {
            Mux oMux = new Mux(this, "merge_mux", iWidth + 1, InputBuses.Count);
            PriorityEncoder oEncode = new PriorityEncoder(this, "merge_encode", InputBuses.Count,oMux.SelectNodes.Width);
            Decoder oDecode = new Decoder(this, "merge_decode", oEncode.OutputNodes.Width, InputBuses.Count);

            oMux.OutputNodes[0, iWidth - 1].Join(oPipeOut.InputBus.Data);
            oMux.OutputNodes[iWidth, iWidth].Join(oPipeOut.InputBus.DataReady);

            oMux.SelectNodes.Join(oEncode.OutputNodes);
            oEncode.OutputNodes.Join(oDecode.InputNodes);

            foreach (int iAddress in dicPipesIn.Keys)
            {
                PipelineStage oPipelineStage = dicPipesIn[iAddress];
                NodeVector nvStall = oPipeOut.InputBus.Stall | (!oDecode.OutputNodes[iAddress,iAddress]);
                oPipelineStage.OutputBus.Stall.Join(nvStall);
                NodeVector nvMuxIn = oMux.InputNodes[iAddress];
                oPipelineStage.OutputBus.Data.Join(nvMuxIn[0, iWidth - 1]);
                oPipelineStage.OutputBus.DataReady.Join(nvMuxIn[iWidth, iWidth]);
                oEncode.InputNodes[iAddress, iAddress].Join(oPipelineStage.OutputBus.DataReady);
            }
        }

    }
}
